package app;

public class DecisionTree {
	private DataSet dataSet;
	private Node rootNode;
	private SymbolicValue defaultValue;
	
	public DecisionTree(DataSet dataSet) {
		if (dataSet == null)
			throw new IllegalArgumentException("Empty data set");
		
		this.dataSet = dataSet;
		this.defaultValue = this.dataSet.findDefaultValue();
		
		rootNode = build(this.dataSet, null);
	}
	
	private Node build(DataSet dataSet, AttributeValue branchValue) {
		SymbolicValue homogenousValue = dataSet.getHomogenousValue();
		if (homogenousValue != null)
			return new LeafNode(homogenousValue, branchValue);
		
		if (dataSet.getAttributeSet().size() == 0)
			return new LeafNode(defaultValue, branchValue);
		
		Attribute splitAttribute = dataSet.findBestSplit();
		
		if (splitAttribute == null)
			return new LeafNode(defaultValue, branchValue);
		
		BranchNode node = new BranchNode(splitAttribute, branchValue);
		DataSet[] splitDataSets = null;
		
		// TODO: this is not right, DataSet should determine the attribute type
		if(splitAttribute instanceof SymbolicAttribute) {
			splitDataSets = dataSet.split(splitAttribute);
		}
		else if (splitAttribute instanceof NumericAttribute) {
			splitDataSets = dataSet.splitNumericAttribute((NumericAttribute)splitAttribute);
		}
		
		for (int i = 0; i < splitDataSets.length; i++) {
			DataSet splitDataSet = splitDataSets[i];
			if(splitAttribute instanceof SymbolicAttribute) {
				SymbolicAttribute symbolicSplitAttribute = (SymbolicAttribute) splitAttribute;
				if (splitDataSet.isEmpty())
					node.addChild(new LeafNode(defaultValue, symbolicSplitAttribute.getValue(i)));
				else
					node.addChild(build(splitDataSet, symbolicSplitAttribute.getValue(i)));
			} else 
				if (splitAttribute instanceof NumericAttribute) {
					NumericAttribute numericSplitAttribute = (NumericAttribute) splitAttribute;
					if (splitDataSet.isEmpty())
						node.addChild(new LeafNode(defaultValue, numericSplitAttribute.getSplitValue()));
					else
						node.addChild(build(splitDataSet, numericSplitAttribute.getSplitValue()));
				}
			
		}
		
		return node;
	}
	
	private void printTree(Node root, String lines) {
		if (root instanceof BranchNode) {
			BranchNode branchRoot = (BranchNode) root;
			
			boolean isLower = true;
			String strSign = " = ";
			for (Node childNode : branchRoot.getChildren()) {
				Attribute attribute = branchRoot.getAttribute();
				if (attribute instanceof NumericAttribute) {
					if (isLower) {
						strSign = " < ";
						isLower = false;
					} else 
						strSign = " >= ";
				}
				
				if (childNode instanceof BranchNode) {
					BranchNode branchNode = (BranchNode) childNode;
					
					System.out.println(lines + branchRoot.getAttribute() + strSign + branchNode.getBranchValue());
					printTree(childNode, lines + "| ");
				} else
					if (childNode instanceof LeafNode) {
						LeafNode leafNode = (LeafNode) childNode;
						System.out.println(lines + branchRoot.getAttribute() + strSign + leafNode.getBranchValue() + ": " + leafNode.getClassValue());
					}
			}
		}
	}
	
	public void printTree() {
		System.out.println("Default value: " + defaultValue);
		printTree(rootNode, "");
	}
}
